package org.example;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.data.shapefile.ShapefileDataStore;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;

import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.geom.GeometryJSON;
import org.opengis.feature.simple.SimpleFeature;
import org.geotools.geojson.feature.FeatureJSON;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.text.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main extends JFrame {
    // 组件声明
    private JButton selectFileButton;
    private JButton convertButton;
    private JTextPane outputArea;  // 改为JTextPane以支持富文本
    private JLabel selectedFileLabel;
    private File selectedFile;
    private StyledDocument doc;
    private SimpleAttributeSet redAttr;
    private SimpleAttributeSet blackAttr;
    private SimpleAttributeSet greenAttr;
    private SimpleAttributeSet blueAttr;
    private static final String FID_FIELD_NAME = "FID"; // FID字段名称
    private static final List<String> NUMERIC_FIELDS = Arrays.asList("A11", "A12");

    private static final String[] fieldsToCheck = { "TBD", "BDH", "CN", "T", "TC", "A11", "A12", "R","BSM"};
    private static final String[] fieldsToCheck2 = { "TBD", "BDH", "CN", "T", "TC", "A11", "A12", "R"};
    private static final List<String> REQUIRED_FIELDS = Arrays.asList(fieldsToCheck2);


    public Main() {
        // 设置窗口基本属性
        setTitle("SHP文件转换器");
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null); // 窗口居中

        // 初始化文本样式
        initStyles();

        // 初始化组件
        initComponents();

        // 设置布局
        setLayout(new BorderLayout(10, 10));

        // 创建顶部面板放置按钮和文件显示
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10));
        topPanel.add(selectFileButton);
        topPanel.add(convertButton);
        topPanel.add(selectedFileLabel);

        // 创建输出区域并添加滚动条
        JScrollPane scrollPane = new JScrollPane(outputArea);
        scrollPane.setBorder(BorderFactory.createTitledBorder("输出信息"));

        // 添加组件到主窗口
        add(topPanel, BorderLayout.NORTH);
        add(scrollPane, BorderLayout.CENTER);

        // 添加事件监听器
        addEventListeners();
    }

    // 初始化文本样式
    private void initStyles() {
        // 红色文字样式
        redAttr = new SimpleAttributeSet();
        StyleConstants.setForeground(redAttr, Color.RED);

        // 默认黑色文字样式
        blackAttr = new SimpleAttributeSet();
        StyleConstants.setForeground(blackAttr, Color.BLACK);

        // 蓝色文字样式 - 强调信息
        blueAttr = new SimpleAttributeSet();
        StyleConstants.setForeground(blueAttr, Color.BLUE);
        StyleConstants.setBold(blueAttr, true);
        // 绿色文字样式 - 成功/存在
        greenAttr = new SimpleAttributeSet();
        StyleConstants.setForeground(greenAttr, new Color(0, 128, 0)); // 深绿色
    }

    private void initComponents() {
        selectFileButton = new JButton("选择SHP文件");
        convertButton = new JButton("转换");
        convertButton.setEnabled(false); // 初始状态禁用
        selectedFileLabel = new JLabel("未选择文件");

        // 使用JTextPane替代JTextArea以支持富文本
        outputArea = new JTextPane();
        outputArea.setEditable(false); // 输出区域不可编辑
        doc = outputArea.getStyledDocument();
    }

    private void addEventListeners() {
        // 文件选择按钮事件
        selectFileButton.addActionListener((ActionEvent e) -> {
            JFileChooser fileChooser = new JFileChooser();

            File defaultFolder = new File("E:\\tempshp\\ybdk\\投保数据");
            if (defaultFolder.exists() && defaultFolder.isDirectory()) {
                fileChooser.setCurrentDirectory(defaultFolder);
            }

            // 创建并添加.shp文件过滤器
            FileNameExtensionFilter filter = new FileNameExtensionFilter(
                    "Shapefiles (.shp)", "shp");
            fileChooser.setFileFilter(filter);

            int result = fileChooser.showOpenDialog(this);

            if (result == JFileChooser.APPROVE_OPTION) {
                selectedFile = fileChooser.getSelectedFile();
                selectedFileLabel.setText("已选择: " + selectedFile.getName());
                convertButton.setEnabled(true);

                // 在输出区域显示文件信息（黑色文字）
                appendText("已选择SHP文件: " + selectedFile.getAbsolutePath() + "\n", blackAttr);
                appendText("文件大小: " + selectedFile.length() + " 字节\n\n", blackAttr);
            } else {
                // 显示取消选择的信息（红色文字）
                appendText("已取消文件选择\n", redAttr);
            }
        });

        // 转换按钮事件
        convertButton.addActionListener((ActionEvent e) -> {
            if (selectedFile != null) {
                appendText("\n开始转换为GeoJSON格式...\n", blueAttr);

                // 先再次确认必填字段（防止文件被修改）
                if (!checkRequiredFields()) {
                    appendText("错误: 检测到必填字段缺失，无法转换。\n", redAttr);
                    return;
                }

                // 执行转换
                boolean conversionSuccess = convertSHPToGeoJSON();
                if (conversionSuccess) {
                    appendText("转换成功！GeoJSON已保存为TXT文件。\n", greenAttr);
//                    beautifyJSON()
                } else {
                    appendText("转换失败，请查看错误信息。\n", redAttr);
                }
            } else {
                appendText("错误: 未选择文件\n", redAttr);
            }
        });
    }

    // 向输出区域添加带样式的文本
    private void appendText(String text, AttributeSet attr) {
        try {
            doc.insertString(doc.getLength(), text, attr);
            // 自动滚动到底部
            outputArea.setCaretPosition(doc.getLength());
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        // 在事件调度线程中运行GUI
        SwingUtilities.invokeLater(() -> {
            new Main().setVisible(true);
        });
    }


    // 检查SHP文件中的字段
    private boolean checkRequiredFields() {
        boolean allPresent = true;
        DataStore dataStore = null;

        try {
            // 读取SHP文件
            Map<String, Object> params = new HashMap<>();
            params.put("url", selectedFile.toURI().toURL());

            dataStore = DataStoreFinder.getDataStore(params);
            if (dataStore == null) {
                appendText("错误: 无法打开SHP文件\n", redAttr);
                return false;
            }

            // 获取要素类型
            String typeName = dataStore.getTypeNames()[0];
            FeatureSource<?, ?> featureSource = dataStore.getFeatureSource(typeName);
            SimpleFeatureType schema = (SimpleFeatureType) featureSource.getSchema();

            // 显示所有字段
            appendText("文件中包含的字段: ", blackAttr);
            List<String> fieldNames = new ArrayList<>();
            for (int i = 0; i < schema.getAttributeCount(); i++) {
                fieldNames.add(schema.getDescriptor(i).getLocalName());
            }
            appendText(String.join(", ", fieldNames) + "\n", blackAttr);

            // 检查必填字段
            appendText("需要检查的必填字段: " + String.join(", ", fieldsToCheck) + "\n", blackAttr);

            for (String field : fieldsToCheck) {
                boolean fieldExists = fieldNames.stream().anyMatch(f -> f.equalsIgnoreCase(field));
                if (fieldExists) {
                    appendText("✓ 存在字段: " + field + "\n", greenAttr);
                } else {
                    appendText("✗ 缺失字段: " + field + "\n", redAttr);
                    allPresent = false;
                }
            }

            return allPresent;

        } catch (Exception ex) {
            appendText("检查字段时出错: " + ex.getMessage() + "\n", redAttr);
            ex.printStackTrace();
            return false;
        } finally {
            if (dataStore != null) {
                dataStore.dispose();
            }
        }
    }

    // 将SHP文件转换为GeoJSON格式并保存为TXT文件
    private boolean convertSHPToGeoJSON() {
        DataStore dataStore = null;
        FeatureIterator<?> iterator = null;

        try {
            // 创建输出文件（与源文件同目录，同名不同扩展名）
            String outputFilePath = selectedFile.getAbsolutePath().replace(".shp", ".geojson.txt");
            File outputFile = new File(outputFilePath);
            // 检查文件是否存在，如果存在则删除
            if (outputFile.exists()) {
                appendText("注意: 目标文件已存在 - " + outputFile.getName() + "\n", redAttr);
                if (outputFile.delete()) {
                    appendText("已删除现有文件\n", blackAttr);
                } else {
                    appendText("错误: 无法删除现有文件，请检查文件权限\n", redAttr);
                    return false; // 如果无法删除文件，终止转换
                }
            }
            // 读取SHP文件
            Map<String, Object> params = new HashMap<>();
            params.put("url", selectedFile.toURI().toURL());

            dataStore = DataStoreFinder.getDataStore(params);
            if (dataStore == null) {
                appendText("错误: 无法打开SHP文件\n", redAttr);
                return false;
            }

            // 设置编码，解决中文乱码问题
            if (dataStore instanceof ShapefileDataStore) {
                ((ShapefileDataStore) dataStore).setCharset(StandardCharsets.UTF_8);
            }

            // 获取要素集合
            String typeName = dataStore.getTypeNames()[0];
            FeatureSource<?, ?> featureSource = dataStore.getFeatureSource(typeName);
            FeatureCollection<?, ?> features = featureSource.getFeatures();

            // 统计要素数量
            int featureCount = 0;
            iterator = features.features();
            while (iterator.hasNext()) {
                featureCount++;
                iterator.next();
            }
            iterator.close(); // 关闭后重新获取迭代器
            iterator = features.features();

            appendText("发现 " + featureCount + " 个要素，正在转换...\n", blackAttr);

            // 使用FeatureJSON写入GeoJSON


            try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)) {
                GeometryJSON gjson = new GeometryJSON(14); // 设置坐标精度为14位小数
                FeatureJSON fjson = new FeatureJSON(gjson);
//                FeatureJSON fjson = createCustomFeatureJSON();
                fjson.setEncodeNullValues(false);
                // 写入GeoJSON开头
                writer.write("{\n");
                writer.write("  \"type\": \"FeatureCollection\",\n");
                writer.write(" \"crs\" : {\n" +
                        "    \"type\" : \"name\",\n" +
                        "    \"properties\" : {\n" +
                        "      \"name\" : \"EPSG:4326\"\n" +
                        "    }\n" +
                        "  },\n");
                writer.write("  \"features\": [\n");
                SimpleFeatureType newFeatureType = null;
                int current = 0;
                while (iterator.hasNext()) {
                    current++;
                    SimpleFeature featureOld = (SimpleFeature) iterator.next();
                    if (newFeatureType == null) {
                        newFeatureType = createNewFeatureType(featureOld.getFeatureType());
                    }
                    checkOld(featureOld);
                    SimpleFeature feature = processFeature(featureOld, newFeatureType, current);
//                    feature=processFeature(feature, current);

                    // 转换单个要素为JSON
                    StringWriter sw = new StringWriter();
                    fjson.writeFeature(feature, sw);
                    org.locationtech.jts.geom.Polygon polygon = (org.locationtech.jts.geom.Polygon) feature.getDefaultGeometry();
                    if(!isPolygonClosed(polygon)){
                        appendText("图形不闭合: BSM:" +featureOld.getAttribute("BSM")+ "\n", redAttr);
                    }
                    if(!isPolygonSimple(polygon)){
                        appendText("图形拓扑错误: BSM:"  +featureOld.getAttribute("BSM")+  "\n", redAttr);
                    }
                    if(!isPolygonValid(polygon)){
                        appendText("图形无效: BSM:"  +featureOld.getAttribute("BSM")+   "\n", redAttr);
                    }
                    // 在现有检查代码后添加
                    if (hasHoles(polygon)) {
                        appendText("图形包含洞: BSM:" + featureOld.getAttribute("BSM") + "\n", redAttr);
                    }
                    String text=removeIdQuotes(sw.toString());
                    // 写入要素，注意处理最后一个要素的逗号
                    writer.write("    " + text);
                    if (current < featureCount) {
                        writer.write(",");
                    }
                    writer.write("\n");

                    // 显示进度
//                    if (current % 10 == 0 || current == featureCount) {
//                        appendText("已转换 " + current + "/" + featureCount + " 个要素\n", blackAttr);
//                    }
                }

                // 写入GeoJSON结尾
                writer.write("  ]\n");
                writer.write("}");
            }

            appendText("GeoJSON已保存至: " + outputFile.getAbsolutePath() + "\n", greenAttr);
            beautifyJSON(outputFile.getAbsolutePath(),outputFile.getAbsolutePath());
            return true;

        } catch (Exception ex) {
            appendText("转换时出错: " + ex.getMessage() + "\n", redAttr);
            ex.printStackTrace();
            return false;
        } finally {
            if (iterator != null) {
                iterator.close();
            }
            if (dataStore != null) {
                dataStore.dispose();
            }
        }
    }

    private static boolean isPolygonValid(org.locationtech.jts.geom.Polygon polygon) {
        if (polygon == null) return true;

        // 检查几何图形是否有效
        return polygon.isValid();
    }

    private static boolean isPolygonSimple(org.locationtech.jts.geom.Polygon polygon) {
        if (polygon == null) return true;

        // 检查几何图形是否简单（不自相交）
        return polygon.isSimple();
    }
    private static boolean hasHoles(org.locationtech.jts.geom.Polygon polygon) {
        if (polygon == null) {
            return false;
        }

        // 检查Polygon是否有内部环(洞)
        return polygon.getNumInteriorRing() > 0;
    }
    private static boolean isPolygonClosed(org.locationtech.jts.geom.Polygon polygon) {
        if (polygon == null) return true;

        org.locationtech.jts.geom.LineString exteriorRing = polygon.getExteriorRing();
        if (exteriorRing != null && !exteriorRing.isClosed()) {
            return false;
        }

        for (int i = 0; i < polygon.getNumInteriorRing(); i++) {
            org.locationtech.jts.geom.LineString interiorRing = polygon.getInteriorRingN(i);
            if (interiorRing != null && !interiorRing.isClosed()) {
                return false;
            }
        }

        return true;
    }
    private static boolean hasMultipleParts(org.locationtech.jts.geom.MultiPolygon multiPolygon) {
        if (multiPolygon == null) {
            return false;
        }

        // 检查MultiPolygon是否包含多个几何部分
        return multiPolygon.getNumGeometries() > 1;
    }

    private static boolean hasHolesInMultiPolygon(org.locationtech.jts.geom.MultiPolygon multiPolygon) {
        if (multiPolygon == null) {
            return false;
        }

        // 检查MultiPolygon中的每个Polygon是否有洞
        for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
            org.locationtech.jts.geom.Polygon polygon = (org.locationtech.jts.geom.Polygon) multiPolygon.getGeometryN(i);
            if (polygon.getNumInteriorRing() > 0) {
                return true;
            }
        }

        return false;
    }
    /**
     * 创建新的要素类型，只包含需要保留的字段和FID
     */
    private static SimpleFeatureType createNewFeatureType(SimpleFeatureType originalType) {
        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        builder.setName(originalType.getName());
        builder.setCRS(originalType.getCoordinateReferenceSystem());

        // 添加几何属性，强制设置为Polygon类型
        org.opengis.feature.type.GeometryDescriptor geomDescriptor = originalType.getGeometryDescriptor();
        if (geomDescriptor != null) {
            // 使用Polygon类型替代原有的几何类型
            builder.add(geomDescriptor.getName().getLocalPart(), org.locationtech.jts.geom.Polygon.class,
                    originalType.getCoordinateReferenceSystem());
        } else {
            // 如果没有几何描述符，添加默认的Polygon类型
            builder.add("the_geom", org.locationtech.jts.geom.Polygon.class,
                    originalType.getCoordinateReferenceSystem());
        }

        // 添加需要保留的属性字段
        for (String field : REQUIRED_FIELDS) {
            Optional<AttributeDescriptor> descriptor = originalType.getAttributeDescriptors().stream()
                    .filter(d -> d.getName().getLocalPart().equals(field))
                    .findFirst();
            if (descriptor.isPresent()) {
                // 确保A11和A12为数字类型
                if (NUMERIC_FIELDS.contains(field)) {
                    Class<?> type = descriptor.get().getType().getBinding();
                    // 如果原类型不是数字类型，强制设为Double
                    if (!Number.class.isAssignableFrom(type)) {
                        builder.add(field, Double.class);
                    } else {
                        builder.add(descriptor.get());
                    }
                } else {
                    builder.add(descriptor.get());
                }
            } else {
                // 如果字段不存在，对于数字字段默认使用Double类型
                if (NUMERIC_FIELDS.contains(field)) {
                    builder.add(field, Double.class);
                } else {
                    builder.add(field, String.class);
                }
            }
        }

        // 添加FID属性
        builder.add(FID_FIELD_NAME, Integer.class);

        return builder.buildFeatureType();
    }

    private void checkOld(SimpleFeature feature){
        Object geometry = feature.getDefaultGeometry();
        org.locationtech.jts.geom.Polygon polygon = null;
        org.locationtech.jts.geom.MultiPolygon multiPolygon = null;

        if (geometry instanceof org.locationtech.jts.geom.Polygon) {
            polygon = (org.locationtech.jts.geom.Polygon) geometry;
            if(!isPolygonClosed(polygon)){
                appendText("图形不闭合: BSM:" +feature.getAttribute("BSM")+ "\n", redAttr);
            }
            if(!isPolygonSimple(polygon)){
                appendText("图形拓扑错误: BSM:"  +feature.getAttribute("BSM")+  "\n", redAttr);
            }
            if(!isPolygonValid(polygon)){
                appendText("图形无效: BSM:"  +feature.getAttribute("BSM")+   "\n", redAttr);
            }
            // 检查Polygon是否有洞
            if (hasHoles(polygon)) {
                appendText("图形包含洞: BSM:" + feature.getAttribute("BSM") + "\n", redAttr);
            }
        } else if (geometry instanceof org.locationtech.jts.geom.MultiPolygon) {
            multiPolygon = (org.locationtech.jts.geom.MultiPolygon) geometry;

            // 检查MultiPolygon是否有效
            if (!multiPolygon.isValid()) {
                appendText("图形无效: BSM:" + feature.getAttribute("BSM") + "\n", redAttr);
            }

            // 检查MultiPolygon是否包含多个部分
            if (hasMultipleParts(multiPolygon)) {
                appendText("图形包含多个部分: BSM:" + feature.getAttribute("BSM") + "\n", redAttr);
            }

            // 检查MultiPolygon是否包含洞
            if (hasHolesInMultiPolygon(multiPolygon)) {
                appendText("图形包含洞: BSM:" + feature.getAttribute("BSM") + "\n", redAttr);
            }
        }
    }
    private static SimpleFeature processFeature(SimpleFeature originalFeature, SimpleFeatureType newFeatureType, int index) {
        // 创建新的属性数组
        List<Object> attributes = new ArrayList<>();

        // 添加几何属性
        // 处理几何属性 - 提取MultiPolygon的第一外环作为Polygon
        Object geometry = originalFeature.getDefaultGeometry();
        if (geometry != null) {
            geometry = extractFirstPolygonFromGeometry(geometry);
        }
        attributes.add(geometry);
        // 添加需要保留的属性
        for (String field : REQUIRED_FIELDS) {
//            attributes.add(originalFeature.getAttribute(field));
            Object value = originalFeature.getAttribute(field);

            // 处理数字字段，确保其为数字类型
            if (NUMERIC_FIELDS.contains(field)) {
                attributes.add(convertToNumber(value));
            } else {
                attributes.add(value);
            }
        }

        // 添加FID属性，值为元素索引
        int fid =  Integer.parseInt(originalFeature.getAttribute("BSM").toString());
        attributes.add(fid);

        // 创建新的要素，设置id为FID的值
        return SimpleFeatureBuilder.build(newFeatureType, attributes.toArray(), String.valueOf(fid));
    }

    /**
     * 从几何对象中提取第一个Polygon
     * 如果是MultiPolygon，提取第一个Polygon
     * 如果是Polygon，直接返回
     * 其他类型保持不变
     */
    private static Object extractFirstPolygonFromGeometry(Object geometry) {
        if (geometry instanceof org.locationtech.jts.geom.MultiPolygon) {
            org.locationtech.jts.geom.MultiPolygon multiPolygon = (org.locationtech.jts.geom.MultiPolygon) geometry;
            if (multiPolygon.getNumGeometries() > 0) {
                System.out.println("提取第一个Polygon");
                // 返回第一个Polygon
                return multiPolygon.getGeometryN(0);
            }
        } else if (geometry instanceof org.locationtech.jts.geom.Polygon) {
            System.out.println("返回Polygon");
            // 如果已经是Polygon，直接返回
            return geometry;
        }else {
            System.err.println("不支持的几何对象类型: " + geometry.getClass().getName());
        }
        // 其他类型的几何对象保持不变
        return geometry;
    }
    /**
     * 将值转换为数字类型，如果无法转换则返回null
     */
    private static Number convertToNumber(Object value) {
        if (value == null) {
            return null;
        }

        if (value instanceof Number) {
            return (Number) value;
        }

        if (value instanceof String) {
            try {
                return Double.parseDouble((String) value);
            } catch (NumberFormatException e) {
                System.err.println("无法将字符串转换为数字: " + value);
                return null;
            }
        }

        System.err.println("不支持的数字类型转换: " + value.getClass().getName());
        return null;
    }
//    /**
//     * 使用正则表达式去除"id":后数字的双引号
//     * 匹配格式: "id": "123" 替换为 "id": 123
//     */
//    private static String removeIdQuotes(String content) {
//        // 正则表达式：匹配 "id": "数字" 格式
//        // 分组1: "id":
//        // 分组2: 数字部分
//        Pattern pattern = Pattern.compile("(\"id\"\\s*:\\s*\")(\\d+)(\")");
//
//        // 替换为 "id": 数字（去掉双引号）
//        Matcher matcher = pattern.matcher(content);
//        return matcher.replaceAll("$1$2");
//    }

    /**
     * 修正id格式，将 "id": "123" 转换为 "id": 123
     */
    private static String removeIdQuotes(String content) {
        // 正则表达式匹配："id": "数字"
        // 考虑可能的空格变化，如 "id" : "456"
        Pattern pattern = Pattern.compile("(\"id\"\\s*:\\s*)\"(\\d+)\"");

        // 替换为："id": 数字（移除前后引号）
        Matcher matcher = pattern.matcher(content);
        return matcher.replaceAll("$1$2");
    }


    private void beautifyJSON(String inputFilePath,String outputFilePath) {
        try {
            // 读取文件内容
            String content = new String(Files.readAllBytes(Paths.get(inputFilePath)));

            // 步骤1：移除id字段的双引号
            String contentWithoutQuotes = removeIdQuotes(content);

            // 步骤2：美化JSON格式
            String prettyContent = beautifyJson(contentWithoutQuotes);

            // 写入处理后的内容
            Files.write(Paths.get(outputFilePath), prettyContent.getBytes());

            appendText("处理完成，美化后的文件已保存至: " + outputFilePath, greenAttr);
        } catch (IOException e) {
            System.err.println("处理失败: " + e.getMessage());
            appendText("处理失败: " + e.getMessage(), redAttr);
            e.printStackTrace();
        }
    }
    /**
     * 美化JSON格式，添加缩进和换行
     */
    private static String beautifyJson(String json) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        // 解析JSON为JsonNode
        JsonNode jsonNode = mapper.readTree(json);
        // 以缩进方式写入JSON字符串
        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
    }
}
